/*
 * Copyright (c) 2023 HPMicro
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 */

 #include "usbd_core.h"
 #include "board.h"
 #include <rtdevice.h>


 #define LCD_WIDTH 800
 #define LCD_HEIGHT 480
 #define PAYLOAD_TOTAL (LCD_WIDTH*LCD_HEIGHT*2)
 
 #define DISPLAY_OUT_EP 0x01

 #define MAX_DISP_PAYLOAD_SIZE (16384)
 extern uint8_t display_read_buffer[MAX_DISP_PAYLOAD_SIZE];

 static volatile uint32_t pix_index = 0;
 
 
 
 
 typedef struct _udisp_frame_header_t { /* 16 bytes */
     uint16_t crc16;//payload crc16
     uint8_t  type; //raw rgb,yuv,jpg,other
     uint8_t  cmd;    
     uint16_t x;  //32bit
     uint16_t y;
     uint16_t width;//32bit
     uint16_t height;
     uint32_t frame_id:10;
     uint32_t payload_total:22; //payload max 4MB
 } __attribute__((packed)) udisp_frame_header_t;
 
 
 #define UDISP_TYPE_RGB565  0
 #define UDISP_TYPE_RGB888  1
 #define UDISP_TYPE_YUV420  2
 #define UDISP_TYPE_JPG	   3
 
 
 //定义 ringbuffer和信号量
 #define RINGBUFFER_SIZE (2048 * 1024)
 static struct rt_ringbuffer usb_rb;
 __attribute__((section(".sdram"))) static uint8_t usb_rb_buffer[RINGBUFFER_SIZE];
 static struct rt_semaphore frame_sem = {0};
 
 
void usbd_graphic_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes)
 {
 
     udisp_frame_header_t * pfh = (udisp_frame_header_t *)display_read_buffer;
     static int total_bytes=0xffff; /*要声明为static类型，很关键*/
 
     if(pix_index==0){
        //  USB_LOG_RAW("frame start");
         total_bytes = pfh->payload_total;
         if(total_bytes < PAYLOAD_TOTAL){
            pix_index=0; 
            goto __exit;
         }
 
 
        //  RT_ASSERT(pfh->width==LCD_WIDTH);
        //  RT_ASSERT(pfh->height==LCD_HEIGHT);
        //  RT_ASSERT(pfh->payload_total==LCD_HEIGHT*LCD_WIDTH*2);
        // USB_LOG_RAW("pfh->wdith=%d\n", pfh->width);
        // USB_LOG_RAW("pfh->height=%d\n", pfh->height);
        // USB_LOG_RAW("pfh->payload_total=%d\n", pfh->payload_total);
 
         switch(pfh->type) {
         case UDISP_TYPE_RGB565:
         case UDISP_TYPE_RGB888:
         case UDISP_TYPE_YUV420:
         case UDISP_TYPE_JPG:
         {
             rt_ringbuffer_put(&usb_rb, &display_read_buffer[sizeof(udisp_frame_header_t)], nbytes - sizeof(udisp_frame_header_t));
             pix_index += nbytes - sizeof(udisp_frame_header_t);
          }
         break;
         default:
             // USB_LOG_RAW("error cmd:%x",pfh->type);
             break;
         }
 
         goto __exit;
     }
 
     rt_ringbuffer_put(&usb_rb, &display_read_buffer[0],nbytes);
     pix_index += nbytes;
 
     if(pix_index >= total_bytes){
         // USB_LOG_RAW("frame end");
         pix_index = 0;
         rt_sem_release(&frame_sem);
         return; /*一帧完成后, 交给线程处理数据, 由线程启动下一packet读取*/
     }
 
 __exit:
     usbd_ep_start_read(0, ep, display_read_buffer, MAX_DISP_PAYLOAD_SIZE);
 }
 
 
 
 static rt_device_t lcd_device = RT_NULL;
 static struct rt_device_graphic_info lcd_info;
 
 /* BMP保存线程 */
 static void udisp_thread_entry(void *parameter)
 {
     rt_sem_init(&frame_sem, "frame_sem", 0, RT_IPC_FLAG_FIFO);
 
     rt_ringbuffer_init(&usb_rb, usb_rb_buffer, RINGBUFFER_SIZE);
     uint8_t * lcdc_buffer = rt_malloc_align(BOARD_LCD_WIDTH * BOARD_LCD_HEIGHT * 2, 32);
     RT_ASSERT(lcdc_buffer != RT_NULL);   
 
     lcd_device = (struct drv_lcd_device *)rt_device_find("lcd0");
     rt_device_open(lcd_device, RT_DEVICE_FLAG_RDWR);
     rt_device_control(lcd_device, RTGRAPHIC_CTRL_GET_INFO, &lcd_info);
     rt_kprintf("lcd_info.width=%d\n",lcd_info.width);
     rt_kprintf("lcd_info.height=%d\n",lcd_info.height);
     rt_kprintf("lcd_info.pixel_format=%d\n",lcd_info.pixel_format);     
     
     while (1) {
         rt_tick_t start_tick = rt_tick_get();
         while(rt_ringbuffer_data_len(&usb_rb) < LCD_HEIGHT*LCD_WIDTH*2){
             rt_sem_take(&frame_sem, RT_WAITING_FOREVER);
         }
         rt_tick_t  end_tick1 = rt_tick_get();
 
         RT_ASSERT(rt_ringbuffer_get(&usb_rb, (rt_uint8_t *)lcd_info.framebuffer, LCD_HEIGHT*LCD_WIDTH*2) == LCD_HEIGHT*LCD_WIDTH*2);
         rt_device_control(lcd_device, RTGRAPHIC_CTRL_RECT_UPDATE, RT_NULL);
 
         rt_tick_t  end_tick2 = rt_tick_get();
 
 
         USB_LOG_RAW("elapse time1=%d ticks\n",end_tick1-start_tick);
         USB_LOG_RAW("elapse time2=%d ticks\n",end_tick2-end_tick1);   
 
         /* 重新启动USB接收 */
         usbd_ep_start_read(0, DISPLAY_OUT_EP, display_read_buffer, MAX_DISP_PAYLOAD_SIZE);


         /*
            elapse time1=47 ticks
            elapse time2=15 ticks
            elapse time1=48 ticks
            elapse time2=15 ticks
            elapse time1=47 ticks
            elapse time2=13 ticks
            elapse time1=48 ticks
            elapse time2=13 ticks
            elapse time1=47 ticks
            elapse time2=16 ticks
            elapse time1=47 ticks
            elapse time2=14 ticks
            elapse time1=47 ticks
            elapse time2=15 ticks
            elapse time1=48 ticks
            elapse time2=14 ticks
            elapse time1=47 ticks
            elapse time2=15 ticks
            elapse time1=47 ticks
            elapse time2=13 ticks
            elapse time1=48 ticks
            elapse time2=13 ticks
            elapse time1=47 ticks
            elapse time2=13 ticks
            elapse time1=47 ticks
            elapse time2=15 ticks
            elapse time1=47 ticks
            elapse time2=14 ticks
            elapse time1=47 ticks
            elapse time2=15 ticks
            elapse time1=47 ticks
            elapse time2=12 ticks
            elapse time1=48 ticks
            elapse time2=14 ticks
            elapse time1=47 ticks
            elapse time2=12 ticks
         */
     }
 }
 
 
 
 int udisp_thread_init(void)
 {
     rt_thread_t udisp_thread = rt_thread_create("udisp", 
                                       udisp_thread_entry, 
                                       RT_NULL, 
                                       4096, 
                                       10, 
                                       20);
    rt_thread_startup(udisp_thread);
 }
 
 INIT_APP_EXPORT(udisp_thread_init);